package prj3;

import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;

import prj3.exceptions.EmptyListException;
import prj3.exceptions.WrongQueryModeException;
import util.Constants;
import prj3.gui.Display;

public class MasterControl {
	
	private static GregorianCalendar originalStartDate;
	private static double percentTemporalStore;
	private static double percentGeographicalStore; 
	private static int precision;
	private static QueryEngine query_engine = null;
	private static BufferedEarthGrid bufEarthGrid = null;
	private static boolean isQuerySystem;
	private static boolean isSimulation;
	private static int state; //0 stopped, running, pause

/** START OF OPERATIONS FOR GUI **/
	
	public static void init(GregorianCalendar defaultStartDate, double percentTempStore, double percentGeoStore, int prec){
		originalStartDate = defaultStartDate;
		percentTemporalStore = percentTempStore;
		percentGeographicalStore = percentGeoStore;
		precision = prec;
		query_engine = new QueryEngine();
		query_engine.setInvocationParameters((float)percentTemporalStore, (float)percentGeographicalStore, precision);
		
	}
	
	public static void setState(int s){
		state = s;
	}
	
//	 TODO send simulation engine kill signal
	public static void pauseSim(){
		System.out.println("!!Pause");
		state = 2;
		if(isSimulation)
			bufEarthGrid.setRunState(state);
	}
	
//	 TODO send simulation engine pause signal
	public static void restartSim(){
		System.out.println("!!Resume");
		state = 1;
		if(isSimulation)
			bufEarthGrid.setRunState(state);
	}
	
	public static void stopSim(){
		System.out.println("!!Stop");
		state = 0;
		if(isSimulation)
			bufEarthGrid.setRunState(state);
	}
	
	
//	This method needs to search in the database first, and if it doesn't
	//find a match, it should then run the simulation.
	public static QueryObject getQueryData(Display gui, boolean getMinTemp, boolean getMaxTemp, 
			boolean getMeanTempForRegion, boolean getMeanTempForTimes, 
			double gridStep, int timeStep, double tilt, double eccentricity, 
			GregorianCalendar startDate, GregorianCalendar endDate, 
			double startLatitude, double endLatitude, double startLongitude, 
			double endLongitude){
		
		state = 1;
		GregorianCalendar curDate = (GregorianCalendar)util.Constants.startDate.clone();
		UpdateObject updateObject = new UpdateObject();
		
		if(query_engine.getSimulationData(tilt, eccentricity, gridStep, 
				timeStep, startDate, endDate, startLatitude, endLatitude, 
				startLongitude, endLongitude)) {
			
			//ITERATE THROUGH ALL GRIDS IN QUERY_ENGINE
			while(curDate.before(endDate)){
				
				while(state == 2){
					Thread.yield();
				}
				
				if(state == 0)
					break; //STOP HACK
			
				/**
				 * Store all of this data to the GUI before dealing w/ the Grid
				 */
				
				try {
					updateObject.simTimeStep = query_engine.takeSimulatedTimeStep();
					updateObject.sunAngle = query_engine.takeSunAngle();
					updateObject.sunDistance = query_engine.takeSunDistance();
					updateObject.sunLat = query_engine.takeSunLatitude();
					updateObject.sunOrbitAngle = query_engine.takeSunOrbitAngle();
				} catch (EmptyListException e) {
					e.printStackTrace();
				}
				
				/**
				 * Now store the Grid
				 */
				
				Iterator iter = query_engine.iterator();
				while(iter.hasNext()){
					while(state == 2){
						//PAUSE HACK
					}
					if(state == 0)
						break; //STOP HACK
					
					SphericalCell cur_cell = (SphericalCell)iter.next();
					EarthCell useCell = null;
					
					try {
						useCell = new EarthCell(cur_cell.myGrid, (SphericalCoordinate)cur_cell.getCoordinate(), cur_cell.takeValue() );
					} catch(EmptyListException ex) {
						ex.printStackTrace();
					}	
					updateObject.curListOfCells.add(useCell);
				}
				
				if(!curDate.before(startDate)){
					gui.updateGUI(updateObject);
				}
				updateObject.curListOfCells.clear();
				
				curDate.add(GregorianCalendar.MINUTE, timeStep);
				try{Thread.sleep(gui.getDisplayRate());} catch(Exception e){}
			}
		} else {
			
			bufEarthGrid = new BufferedEarthGrid((float)6348, (float)gridStep, (float)eccentricity, (float)tilt, 10);
			
			query_engine.startSimulationInput((float)tilt, (float)eccentricity, (float)gridStep, (float)timeStep);
			isSimulation = true;
			// Now start the simulation in a separate thread
			bufEarthGrid.setStepSize(timeStep);
			bufEarthGrid.setMaxIterations((long)(endDate.getTimeInMillis() - curDate.getTimeInMillis()/1000)/timeStep);
			new Thread(bufEarthGrid).start();
			
			// First deal with all the input that we don't need for the output
			// We still store it though
			
			while(curDate.before(startDate)) {
				
				GregorianCalendar dd = null;
				boolean gotSomething = false;
				while(!gotSomething) {
					try {
						dd = bufEarthGrid.takeSimulatedTimeStep();
						gotSomething = true;
					} catch(EmptyListException e) {
						if(state == 0)
							break; // We stopped so that's it
						try {
							Thread.sleep(10);
						} catch(InterruptedException ex) {}
					}
				}
				
				query_engine.setSimulatedTimeStep(dd);
				
				Iterator iter = bufEarthGrid.iterator();
				while(iter.hasNext()){
					
					SphericalCell cur_cell = (SphericalCell)iter.next();
					EarthCell useCell = null;
					
					try {
						useCell = new EarthCell(cur_cell.myGrid, (SphericalCoordinate)cur_cell.getCoordinate(), cur_cell.takeValue() );
					} catch(EmptyListException ex) {
						ex.printStackTrace();
					}		
					
					//We are going to store the data from the simulation no matter what
					try {
						query_engine.inputCell(useCell);
					} catch (WrongQueryModeException e) {
						e.printStackTrace();
					}

				}
				curDate.add(GregorianCalendar.MINUTE, timeStep);
				//bufEarthGrid.stepGrid(timeStep);
			}
			
			// Now we actually display things
			while(curDate.before(endDate)){
				boolean gotSomething = false;
				while(!gotSomething) {

					try {
						updateObject.simTimeStep = bufEarthGrid.takeSimulatedTimeStep();
						updateObject.sunAngle = bufEarthGrid.takeSunAngle();
						updateObject.sunDistance = bufEarthGrid.takeSunDistance();
						updateObject.sunLat = bufEarthGrid.takeSunLatitude();
						updateObject.sunOrbitAngle = bufEarthGrid.takeSunOrbitAngle();
						gotSomething = true;
					} catch (EmptyListException e) {
						if(state == 0)
							break; // We stopped so that's it
						try {
							Thread.sleep(10);
						} catch(InterruptedException ex) {}
					}
				}
				//Open input stream to database
				query_engine.setSimulatedTimeStep(updateObject.simTimeStep);
				
				Iterator iter = bufEarthGrid.iterator();
				while(iter.hasNext()){
					
					SphericalCell cur_cell = (SphericalCell)iter.next();
					EarthCell useCell = null;
					try {
						useCell = new EarthCell(cur_cell.myGrid, (SphericalCoordinate)cur_cell.getCoordinate(), cur_cell.takeValue() );
					} catch(EmptyListException ex) {
						ex.printStackTrace();
					}
					
					updateObject.curListOfCells.add(useCell);
					
					//We are going to store the data from the simulation no matter what
					try {
						query_engine.inputCell(useCell);
					} catch (WrongQueryModeException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
				gui.updateGUI(updateObject);
				
				updateObject.curListOfCells.clear();
				
				//Close input stream to database
				
				curDate.add(GregorianCalendar.MINUTE, timeStep);
				//bufEarthGrid.stepGrid(timeStep);
				
				try{Thread.sleep(gui.getDisplayRate());} catch(Exception e){}
			}
			
			query_engine.stopSimulationInput();
			bufEarthGrid.setRunState(0);
			isSimulation = false;
		}
		
		QueryObject queryObject = new QueryObject();
		
		if(query_engine.getSimulationData(tilt, eccentricity, gridStep, 
				timeStep, startDate, endDate, startLatitude, endLatitude, 
				startLongitude, endLongitude)){
				
			queryObject.maxTemp = query_engine.getMaxTemperature();
			queryObject.maxTempLat = query_engine.getMaxTemperatureLatitude();
			queryObject.maxTempLong = query_engine.getMaxTemperatureLongitude();
			queryObject.maxTempTime = query_engine.getMaxTemperatureTime();
			queryObject.minTemp = query_engine.getMinTemperature();
			queryObject.minTempLat = query_engine.getMinTemperatureLatitude();
			queryObject.minTempLong = query_engine.getMinTemperatureLongitude();
			queryObject.minTempTime = query_engine.getMinTemperatureTime();
			
			queryObject.avgForRegion = new LinkedList();
			queryObject.avgForTimes = new LinkedList();
			
			Iterator buf_iter = query_engine.iterator();
			while(buf_iter.hasNext()){
				BufferedEarthCell cur_buf_cell = (BufferedEarthCell)buf_iter.next();
				
				queryObject.avgForRegion.add(cur_buf_cell);
				queryObject.avgForRegion.add(new Float(query_engine.getAvgTemperatureForCell(cur_buf_cell)));			
			}
			
			curDate = startDate;
			while(curDate.before(endDate)) {
				queryObject.avgForTimes.add(curDate.clone());
				queryObject.avgForTimes.add(query_engine.getAvgTemperatureForTime(curDate));
				curDate.add(GregorianCalendar.MINUTE, timeStep);
			}
		}

		return queryObject;
	}

}
